package com.wxzhou.wangzhuan.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.wxzhou.wangzhuan.dto.*;
import com.wxzhou.wangzhuan.dto.config.WithdrawAmountLimitDto;
import com.wxzhou.wangzhuan.dto.config.WithdrawConfigDto;
import com.wxzhou.wangzhuan.dto.config.WithdrawTotalLimitDto;
import com.wxzhou.wangzhuan.dto.pay.HuifuSchemeRespDto;
import com.wxzhou.wangzhuan.dto.pay.HuifuTaxRespDto;
import com.wxzhou.wangzhuan.entity.*;
import com.wxzhou.wangzhuan.exception.UserRealNameException;
import com.wxzhou.wangzhuan.exception.UserTaxAgrementException;
import com.wxzhou.wangzhuan.exception.WithdrawException;
import com.wxzhou.wangzhuan.repository.UserAccountRepository;
import com.wxzhou.wangzhuan.repository.WithdrawRepository;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@ApplicationScoped
public class WithdrawService {

    private static final Logger LOG = Logger.getLogger(WithdrawService.class);

    @Inject
    ConfigService configService;

    @Inject
    UserAccountRepository userAccountRepository;

    @Inject
    WithdrawCacheService withdrawCacheService;

    @Inject
    WithdrawRepository withdrawRepository;

    @Inject
    PayService payService;

    @Inject
    UserAccountService userAccountService;

    @Inject
    ObjectMapper objectMapper;

    @Inject
    private WithdrawRedisPubService withdrawRedisPubService;

    /**
     * 最大轮询校验次数
     */
    @ConfigProperty(name = "huifu.scheme.max_count", defaultValue = "10")
    int maxWithdrawCheckCount;

    /**
     * 间隔2秒检查1次
     */
    @ConfigProperty(name = "huifu.scheme.seconds", defaultValue = "1")
    int checkDaliySeconds;


    /**
     * 检查用户是否已经绑定提现信息！如果已经绑定，则返回
     *
     * @param bindRequestDto
     * @param userInfoDto
     * @return
     */
    public Uni<UserBindWithdrawCheckResponseDto> checkBindWithdraw(UserBindWithdrawRequestDto bindRequestDto, UserInfoDto userInfoDto) {
        int paymentCode = PaymentType.fromCode(bindRequestDto.getPaymentType()).getCode();
        Long userId = userInfoDto.getUserId();

        return checkUserBindStatusBy(String.valueOf(paymentCode), userId).onItem().transformToUni(hasBind -> {
            if (!hasBind) {
                // 未绑定，调用三方支付，校验绑定结果
                return payService.getScheme(userId).onItem().transformToUni(huifuResponseDto -> {
                    HuifuSchemeRespDto data = (HuifuSchemeRespDto) huifuResponseDto.getData();
                    if (data.getIs_auth() == 0) {
                        // 未绑定，尝试获取分布式锁进行异步轮询
                        return withdrawCacheService.acquireBindWithdrawLock(userId, paymentCode).onItem().transformToUni(lockRequestId -> {
                            if (lockRequestId != null) {
                                // 成功获取锁，启动异步轮询
                                startAsyncPollingForBindResult(userId, paymentCode, lockRequestId);
                                // 立即返回未绑定状态和scheme信息
                                return Uni.createFrom().item(UserBindWithdrawCheckResponseDto.notBind(data.getScheme()));
                            } else {
                                // 获取锁失败，说明正在检查绑定结果，抛出异常
                                throw new RuntimeException("正在检查绑定结果，请稍后再试");
                            }
                        });
                    } else {
                        // 特殊情况：检查到未绑定，但实际三方已绑定，直接插入数据
                        UserBindWithdrawRelation bindWithdrawRelation = UserBindWithdrawRelation.createByThirdPart(userId, paymentCode, "已绑定", "");
                        // 异步插入数据，无论成功失败都继续返回结果
                        withdrawRepository.insertBindWithdrawResult(bindWithdrawRelation).subscribe().with(result -> LOG.info("用户绑定信息插入成功"), failure -> LOG.error("用户绑定信息插入失败: " + failure.getMessage()));
                        return Uni.createFrom().item(UserBindWithdrawCheckResponseDto.hasBind());
                    }
                });
            } else {
                return Uni.createFrom().item(UserBindWithdrawCheckResponseDto.hasBind());
            }
        });
    }

    /**
     * 启动异步轮询获取绑定结果
     *
     * @param userId        用户ID
     * @param paymentCode   支付类型编码
     * @param lockRequestId 锁请求ID
     */
    private void startAsyncPollingForBindResult(Long userId, Integer paymentCode, String lockRequestId) {
        // 启动轮询
        pollBindResultAsync(userId, paymentCode, lockRequestId, 0);
    }

    /**
     * 异步轮询获取绑定结果
     *
     * @param userId        用户ID
     * @param paymentCode   支付类型编码
     * @param lockRequestId 锁请求ID
     * @param currentCount  当前轮询次数
     */
    private void pollBindResultAsync(Long userId, Integer paymentCode, String lockRequestId, int currentCount) {
        // 检查是否超过最大轮询次数
        if (currentCount >= maxWithdrawCheckCount) {
            // 释放锁并结束轮询
            withdrawCacheService.releaseBindWithdrawLock(userId, paymentCode, lockRequestId).onItem().invoke(() -> LOG.info("绑定轮询超时，释放锁")).onFailure().invoke(failure -> LOG.error("释放锁失败: " + failure.getMessage())).subscribe();
            return;
        }

        // 延迟1秒后再次查询
        Uni.createFrom().voidItem().onItem().delayIt().by(Duration.ofSeconds(checkDaliySeconds)).onItem().transformToUni(v -> payService.getScheme(userId)).subscribe().with(huifuResponseDto -> {
            HuifuSchemeRespDto data = (HuifuSchemeRespDto) huifuResponseDto.getData();
            if (data.getIs_auth() > 0) {
                // 绑定成功，插入数据
                UserBindWithdrawRelation bindWithdrawRelation = UserBindWithdrawRelation.createByThirdPart(userId, paymentCode, "已绑定", "");
                // 异步插入数据，无论成功失败都插入
                withdrawRepository.insertBindWithdrawResult(bindWithdrawRelation).subscribe().with(result -> LOG.info("用户绑定信息插入成功"), failure -> LOG.error("用户绑定信息插入失败: " + failure.getMessage()));
                // 释放锁
                withdrawCacheService.releaseBindWithdrawLock(userId, paymentCode, lockRequestId).onItem().invoke(() -> LOG.info("绑定成功，释放锁")).onFailure().invoke(failure -> LOG.error("释放锁失败: " + failure.getMessage())).subscribe();
            } else {
                // 继续轮询
                pollBindResultAsync(userId, paymentCode, lockRequestId, currentCount + 1);
            }
        }, failure -> {
            LOG.error("轮询绑定结果失败: " + failure.getMessage());
            // 继续轮询
            pollBindResultAsync(userId, paymentCode, lockRequestId, currentCount + 1);
        });
    }

    /**
     * 检查用户绑定状态
     *
     * @param paymentCode
     * @param userId
     * @return
     */
    private Uni<Boolean> checkUserBindStatusBy(String paymentCode, Long userId) {
        return withdrawRepository.findUserWithdrawRelationByUser(userId).onItem().transformToUni(realnameRelations -> {
            Map<String, UserBindWithdrawRelation> paymentAndRealNames = realnameRelations.stream().collect(Collectors.collectingAndThen(Collectors.groupingBy(o -> String.valueOf(o.getPaymentType()), Collectors.collectingAndThen(Collectors.toList(), list -> list.isEmpty() ? null : list.get(0))), map -> {
                map.values().removeIf(value -> value == null);
                return map;
            }));
            if (!paymentAndRealNames.containsKey(String.valueOf(paymentCode))) {
                return Uni.createFrom().item(false);
            } else {
                return Uni.createFrom().item(true);
            }

        });
    }

    /**
     * 提交实名资料
     *
     * @param bindRequestDto
     * @param userInfoDto
     * @return
     */
    /**
     * 异步提交实名信息
     * 方法立即返回成功，实际处理逻辑在后台异步执行
     * 目的是让客户端无需等待耗时操作完成
     */
    public Uni<Boolean> realnameSubmit(RealNameBindRequestDto bindRequestDto, UserInfoDto userInfoDto) {
        Long userId = userInfoDto.getUserId();
        int paymentCode = PaymentType.fromCode(bindRequestDto.getPaymentType()).getCode();

        // 立即返回成功，告知客户端请求已接收
        // 真正的处理逻辑在后台异步执行
        Uni.createFrom().voidItem().onItem().transformToUni(v -> {
                    // 1. 尝试获取分布式锁
                    return withdrawCacheService.acquireRealNameLock(userId).onItem().transformToUni(lockRequestId -> {
                        if (lockRequestId == null) {
                            // 获取锁失败，记录日志但不影响返回
                            LOG.warnf("用户[%s]实名提交请求处理中，请稍后再试", userId);
                            return Uni.createFrom().voidItem();
                        }

                        // 2. 异步处理实名提交逻辑，确保无论结果如何都释放锁
                        return processRealNameSubmissionWithLock(bindRequestDto, userId, paymentCode).onTermination().invoke(() -> {
                                    // 无论成功失败，最终都释放锁
                                    withdrawCacheService.releaseRealNameLock(userId, lockRequestId).subscribe().with(v2 -> LOG.infof("用户[%s]实名提交锁已释放", userId));
                                })
                                // 忽略处理结果，因为我们已经立即返回了
                                .replaceWithVoid();
                    }).onFailure().invoke(error -> {
                        LOG.error("用户[%s]实名提交处理异常", userId, error);
                    });
                })
                // 在后台启动异步流程，但不阻塞返回
                .subscribe().with(v -> LOG.infof("用户[%s]实名提交请求已接收并开始后台处理", userId), error -> LOG.error("启动实名提交异步处理失败", error));

        // 立即返回成功，表示请求已接收
        return Uni.createFrom().item(true);
    }

    /**
     * 带锁的实名提交处理逻辑
     */
    private Uni<Boolean> processRealNameSubmissionWithLock(RealNameBindRequestDto bindRequestDto, Long userId, int paymentCode) {
        // 检查是否已经实名
        return checkRealNameBy(String.valueOf(paymentCode), userId).onItem().transformToUni(hasRealName -> {
            if (Boolean.TRUE.equals(hasRealName)) {
                // 已实名，无需重复提交
                LOG.infof("用户%s已完成实名绑定，无需重复提交", userId);
                return Uni.createFrom().item(true);
            }

            // 未实名，执行实名校验流程
            return submitRealNameToThirdParty(bindRequestDto, userId, paymentCode);
        });
    }

    /**
     * 提交实名信息到第三方并处理结果
     */
    private Uni<Boolean> submitRealNameToThirdParty(RealNameBindRequestDto bindRequestDto, Long userId, int paymentCode) {
        RealNameInfo realNameInfo = bindRequestDto.getInfo();

        // 添加延迟测试功能（10秒）
        LOG.infof("用户[%s]开始延迟10秒测试...", userId);
        //测试期间用延迟10秒验证异步执行是否OK
//        return Uni.createFrom().voidItem().onItem().delayIt().by(Duration.ofSeconds(10)).onItem().transformToUni(v -> {
        return Uni.createFrom().voidItem().onItem().transformToUni(v -> {
            // 提交到三方进行实名校验
            LOG.infof("用户[%s]延迟结束，开始提交到三方进行实名校验", userId);
            return payService.submitRealName(userId, realNameInfo).onItem().transformToUni(huifuResponseDto -> {
                if (huifuResponseDto.isError()) {
                    LOG.errorf("实名绑定失败,错误代码:%s", huifuResponseDto.getCode());
                    throw new UserRealNameException("实名绑定失败,错误代码:" + huifuResponseDto.getCode());
                }

                // 提交成功，持久化到数据库
                return saveRealNameInfoToDatabase(userId, paymentCode, realNameInfo);
            });
        });
    }

    /**
     * 保存实名信息到数据库
     */
    private Uni<Boolean> saveRealNameInfoToDatabase(Long userId, int paymentCode, RealNameInfo realNameInfo) {
        try {
            String realInfoDetails = objectMapper.writeValueAsString(realNameInfo);
            UserRealnameRelation realnameRelation = UserRealnameRelation.createByThirdPart(userId, paymentCode, realNameInfo.getRealname(), realInfoDetails);

            return userAccountRepository.insertUserRealNameResult(realnameRelation).onItem().invoke(relation -> {
                LOG.infof("用户[%s]实名信息保存成功", userId);
            }).onItem().transform(relation -> true).onFailure().invoke(error -> {
                LOG.errorf("保存实名数据到数据库失败", error);
            }).onFailure().recoverWithUni(error -> {
                throw new UserRealNameException("保存实名数据到数据库失败", error);
            });
        } catch (Exception e) {
            LOG.errorf("转换用户实名对象到string错误", e);
            throw new UserRealNameException("转换用户实名信息失败", e);
        }
    }

    /**
     * 校验用户提现时,某个到账渠道是否实名,如果未实名,返回false,如果已实名,则返回true
     *
     * @param paymentCode 提现渠道
     * @return
     */
    private Uni<Boolean> checkRealNameBy(String paymentCode, Long userId) {
        return userAccountService.findRealNameRelationByUser(userId).onItem().transformToUni(realnameRelations -> {
            Map<String, UserRealnameRelation> paymentAndRealNames = realnameRelations.stream().collect(Collectors.collectingAndThen(Collectors.groupingBy(o -> String.valueOf(o.getPaymentType()), Collectors.collectingAndThen(Collectors.toList(), list -> list.isEmpty() ? null : list.get(0))), map -> {
                map.values().removeIf(value -> value == null);
                return map;
            }));
            if (!paymentAndRealNames.containsKey(String.valueOf(paymentCode))) {
                return Uni.createFrom().item(false);
            } else {
                return Uni.createFrom().item(true);
            }

        });
    }

    /**
     * 查询1个月内的用户提现记录
     *
     * @param pageRequest
     * @return
     */
    public Uni<PageResponseDto<WithdrawRecordResponseDto>> getPageList(WithdrawRecordRequestDto pageRequest, UserInfoDto currentUserInfo) {
        pageRequest.setUserid(currentUserInfo.getUserId());
        Uni<List<WithdrawRecordResponseDto>> dataUni = withdrawRepository.findWithPagination(pageRequest);
        Uni<Long> countUni = withdrawRepository.countOf(pageRequest);
        return Uni.combine().all().unis(dataUni, countUni).asTuple().onItem().transform(tuple -> {
            List<WithdrawRecordResponseDto> responseDtos = tuple.getItem1();
            Integer total = tuple.getItem2().intValue();
            return PageResponseDto.of(pageRequest.getPage(), pageRequest.getLimit(), total, responseDtos);
        });
    }

    /**
     * 用户同意交税协议
     * 调用三方支付接口同步进行校验
     *
     * @param currentUserInfo
     * @return
     */
    public Uni<Boolean> taxAgreement(UserInfoDto currentUserInfo) {
        return payService.taxAgreement(currentUserInfo.getUserId()).onItem()
                .transformToUni(huifuResponseDto -> {
                    boolean result = huifuResponseDto != null && huifuResponseDto.isSuccess() ? true : false;
                    return withdrawCacheService.delTaxAgreement(currentUserInfo.getUserId())
                            .onItem().transform(aBoolean -> result);
                });
    }

    /**
     * 查询汇付八方预估到账金额
     *
     * @param withdrawRequestDto
     * @param currentUserInfo
     * @return
     */
    public Uni<HuifuTaxRespDto> getTax(WithdrawRequestDto withdrawRequestDto, UserInfoDto currentUserInfo) {
        BigDecimal withdrawAmountFen = withdrawRequestDto.getAmount().multiply(new BigDecimal("100")); //提现金额,转分
        return payService.getTax(currentUserInfo.getUserId(), withdrawAmountFen.intValue()).onItem()
                .transform(huifuResponseDto -> {
                    if (huifuResponseDto != null && huifuResponseDto.isSuccess()) {
                        HuifuTaxRespDto data = huifuResponseDto.getData();
                        if (data.getReceived_price() > 0) {
                            data.setReceived_price(new BigDecimal(data.getReceived_price()).divide(new BigDecimal("100"), 2, RoundingMode.DOWN).intValue());
                        }
                        return data;
                    }
                    return new HuifuTaxRespDto();
                });
    }

    /**
     * 查询用户提现信息绑定状态
     *
     * @param userId
     * @return
     */
    public Uni<List<UserBindWithdrawRelation>> findUserWithdrawRelationByUser(Long userId) {
        return withdrawRepository.findUserWithdrawRelationByUser(userId);
    }


    /**
     * 查询当前用户提现面板数据
     *
     * @param currentUserInfo
     * @return
     */
    public Uni<WithdrawDashboardResponseDto> getWithdrawDashboard(UserInfoDto currentUserInfo) {
        Uni<UserAccountDashboardResponseDto> accountDashboardByUser = userAccountService.getAccountDashboardByUser(currentUserInfo);
        Uni<List<UserBindWithdrawRelation>> userWithdrawRelationByUser = findUserWithdrawRelationByUser(currentUserInfo.getUserId());
        // 先获取并校验提现全局配置
        Uni<WithdrawConfigDto> withdrawConfig = configService.getWithdrawConfig();

        return Uni.combine().all().unis(accountDashboardByUser, withdrawConfig, userWithdrawRelationByUser).asTuple().onItem().transform(tuple -> {
            UserAccountDashboardResponseDto accountDashboardResponseDto = tuple.getItem1();
            WithdrawConfigDto configDto = tuple.getItem2();
            List<UserBindWithdrawRelation> userRealnameRelations = tuple.getItem3(); //实名记录,按到账渠道进行分组
            Map<String, UserBindWithdrawRelation> paymentAndRealName = userRealnameRelations.stream().collect(Collectors.collectingAndThen(Collectors.groupingBy(o -> String.valueOf(o.getPaymentType()), Collectors.collectingAndThen(Collectors.toList(), list -> list.isEmpty() ? null : list.get(0))), map -> {
                map.values().removeIf(value -> value == null);
                return map;
            }));

            WithdrawDashboardResponseDto dto = new WithdrawDashboardResponseDto();
            //TODO:可以在此处动态过滤用户已提现的金额,过滤掉今日不可提现的金额.
            Map<String, List<WithdrawAmountLimitDto>> amountLimits = configDto.getAmountLimits();
            Map<String, WithdrawAmountListDto> result = amountLimits.entrySet().stream().map(entry -> {
                String paymentType = entry.getKey();
                List<WithdrawAmountLimitDto> value = entry.getValue();

                // 将配置的提现金额列表转换为前端所需的DTO列表
                List<WithdrawAmountListItemDto> withdrawAmountListItemDtos = value.stream().map(withdrawAmountLimitDto -> {
                    WithdrawAmountListItemDto listDto = new WithdrawAmountListItemDto();
                    listDto.setAmount(withdrawAmountLimitDto.getAmount());
                    listDto.setBadge(withdrawAmountLimitDto.getBadge());
                    return listDto;
                }).collect(Collectors.toList());

                // 创建当前支付渠道的提现金额列表DTO
                WithdrawAmountListDto amountListDto = new WithdrawAmountListDto();
                amountListDto.setAmountList(withdrawAmountListItemDtos);

                // 检查用户是否有该支付渠道的绑定信息
                if (paymentAndRealName != null && paymentAndRealName.containsKey(paymentType)) {
                    // 如果有班定信息，设置绑定状态和班定文本
                    amountListDto.setHasBind(true);
                    UserBindWithdrawRelation bindWithdrawRelation = paymentAndRealName.get(paymentType);
                    // 设置简单的班定文本显示（例如显示部分姓名）
                    amountListDto.setSimpleText(bindWithdrawRelation.getSimpleName());
                } else {
                    // 如果没有班定信息，设置为未绑定状态
                    amountListDto.setHasBind(false);
                }

                return new AbstractMap.SimpleEntry<>(paymentType, amountListDto);
            }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

            // 如果用户没有任何渠道的实名信息，则只返回可提现列表（已在上面处理）
            // 如果用户有实名信息，则同时返回可提现列表和实名记录（也已在上面处理）
            dto.setBalanceCny(accountDashboardResponseDto.getBalanceCny());
            dto.setWithdraws(result);
            return dto;
        });
    }

    /**
     * 提现申请
     * 处理逻辑: 校验提现申请的dto、获取提现配置并校验当前是否允许提现、根据用户查询用户当前提现权限、根据支付配置找到提现路由（根据系统支付配置和用户提现渠道）
     *
     * @param withdrawRequestDto
     * @param currentUserInfo
     * @return
     */
    public Uni<WithdrawResponseDto> request(WithdrawRequestDto withdrawRequestDto, UserInfoDto currentUserInfo) {
        // 先获取并校验提现全局配置
        return configService.getWithdrawConfig().onItem().transform(withdrawConfig -> {
                    // 校验提现配置
                    validWithdraw(withdrawConfig);
                    // 校验通过，传递配置给下一个调用链
                    return withdrawConfig;
                }).onItem().transform(withdrawConfigDto -> {
                    return withdrawConfigDto;
                })
                // 校验通过后，获取用户信息
                .chain(withdrawConfig -> userAccountRepository.findByUserId(currentUserInfo.getUserId())
                        .chain(userAccountWithUser -> validAndSendWithdrawRequestReactive(userAccountWithUser, withdrawConfig, withdrawRequestDto))
                        .onItem().transform(summary -> new WithdrawResponseDto()));
    }


    /**
     * 校验系统提现
     *
     * @param withdrawConfig
     */
    private void validWithdraw(WithdrawConfigDto withdrawConfig) {
        // 实现提现配置校验逻辑
        // 校验失败时抛出异常
        if (withdrawConfig == null || withdrawConfig.getWithdrawInterval() == null || withdrawConfig.getDailyMaxCount() == null) {
            throw new WithdrawException("提现配置不存在");
        }

        // 1. 校验总开关
        if (!Boolean.TRUE.equals(withdrawConfig.getWithdrawEnabled())) {
            throw new WithdrawException("提现功能暂未开启");
        }

        // 2. 校验可提时间
        LocalTime now = LocalTime.now();
        LocalTime startTime = withdrawConfig.getStartTime();
        LocalTime endTime = withdrawConfig.getEndTime();

        // 校验时间配置不能为空
        if (startTime == null || endTime == null) {
            throw new WithdrawException("提现时间配置不能为空");
        }

        // 检查当前时间是否在可提时间范围内
        if (now.isBefore(startTime) || now.isAfter(endTime)) {
            throw new WithdrawException(String.format("提现时间为每日%s-%s", startTime.format(DateTimeFormatter.ofPattern("HH:mm")), endTime.format(DateTimeFormatter.ofPattern("HH:mm"))));
        }
    }

    /**
     * 响应式版本的校验方法，避免使用阻塞操作
     *
     * @param userAccountWithUser 用户信息和账户信息
     * @param withdrawConfig      系统配置
     * @param withdrawRequestDto  用户提交的提现申请对象
     * @return Uni<WithdrawSummary> 提现摘要信息
     */
    private Uni<WithdrawSummary> validAndSendWithdrawRequestReactive(UserAccountWithUser userAccountWithUser, WithdrawConfigDto withdrawConfig, WithdrawRequestDto withdrawRequestDto) {
        // 0. 校验账户是否存在
        if (userAccountWithUser == null || userAccountWithUser.getUserId() == null || userAccountWithUser.getUserId() <= 0L || userAccountWithUser.getId() == null || userAccountWithUser.getId() <= 0L) {
            return Uni.createFrom().failure(new WithdrawException("用户账户不存在"));
        }

        // 1. 校验用户是否允许提现、账户状态是否正常
        if (Boolean.FALSE.equals(userAccountWithUser.getUser_is_enable())) {
            return Uni.createFrom().failure(new WithdrawException("用户账号已禁用"));
        }
        if (Boolean.FALSE.equals(userAccountWithUser.getUser_is_withdrawal())) {
            return Uni.createFrom().failure(new WithdrawException("用户暂无提现权限"));
        }
        if (userAccountWithUser.getBalance() == null || userAccountWithUser.getBalance().compareTo(BigDecimal.ZERO) <= 0) {
            return Uni.createFrom().failure(new WithdrawException("账户余额不足"));
        }

        // 2. 校验提现金额是否足够(单位分)
        BigDecimal withdrawAmount = withdrawRequestDto.getAmount();
        if (withdrawAmount == null || withdrawAmount.compareTo(BigDecimal.ZERO) <= 0) {
            return Uni.createFrom().failure(new WithdrawException("提现金额必须大于0"));
        }
        //转换成元，比较金额
        if (withdrawAmount.multiply(new BigDecimal("100")).compareTo(userAccountWithUser.getBalance()) > 0) {
            return Uni.createFrom().failure(new WithdrawException("账户余额不足"));
        }


        // 3. 响应式获取用户提现统计信息进行进一步校验
        return withdrawCacheService.findWithdrawSummaryByUser(userAccountWithUser.getUserId()).onItem().ifNull().failWith(() -> {
                    LOG.errorf("获取当前用户提现记录异常 用户id = %s", userAccountWithUser.getUserId());
                    return new WithdrawException("获取用户提现统计信息失败，系统异常");
                }).onItem().transformToUni(withdrawSummary -> {
                    return withdrawCacheService.checkWithdrawLock(userAccountWithUser.getUserId()).onItem().transformToUni(s -> {
                        if (s != null) {
                            LOG.errorf("用户%s锁已经存在，不允许提现", userAccountWithUser.getUserId());
                            throw new WithdrawException("尚有提现未处理，请勿重复提现");
                        }
                        return Uni.createFrom().item(withdrawSummary);
                    });
                })
                .onItem().transformToUni(withdrawSummary -> {
                    return withdrawCacheService.getTaxAgreement(userAccountWithUser.getUserId()).onItem()
                            .transform(existsLockTaxAgreement -> {
                                if (existsLockTaxAgreement) {
                                    throw new UserTaxAgrementException("用户需同意交税协议");
                                }
                                return withdrawSummary;
                            });
                })
                .onItem().transformToUni(withdrawSummary -> validWithdrawStepOne(withdrawConfig, withdrawSummary, withdrawAmount))
                .onItem().transformToUni(withdrawSummary -> validWithdrawStepTwo(userAccountWithUser, withdrawSummary))
                .onItem().transformToUni(withdrawSummary -> validWithdrawStepThree(userAccountWithUser, withdrawConfig, withdrawRequestDto, withdrawSummary, withdrawAmount))
                .onItem().transform(withdrawSummary -> {
                    //9：所有情况都成功时，异步持久化到数据库存入用户提现记录中并根据状态写入缓存，比如自动放行，即可开始交易，写入缓存前，必须确保数据库持久化数据正常！
                    Boolean autoApproveEnabled = withdrawConfig.getAutoApproveEnabled();
                    BigDecimal autoApproveThreshold = withdrawConfig.getAutoApproveThreshold(); //放行阈值
                    //如果系统配置了自动放行，并且提现金额小于等于自动放行阈值，则直接放行
                    boolean isAuto = autoApproveEnabled == Boolean.TRUE && withdrawAmount.compareTo(autoApproveThreshold) <= 0;

                    // 异步调用，不阻塞返回
                    saveWithdrawInfoAndCache(withdrawAmount, isAuto, userAccountWithUser.getUserId()).subscribe()
                            .with(
                                    success -> {
                                        LOG.debugf("用户%s提现申请保存%s", userAccountWithUser.getUserId(), success ? "成功" : "失败");
                                    },
                                    failure -> {
                                        LOG.errorf(failure, "用户%s提现申请异步保存失败", userAccountWithUser.getUserId());
                                    });

                    // 立即返回，不等待异步操作完成
                    return withdrawSummary;
                });
    }

    /**
     * 校验第三步，校验是否达到实名阈值，并且校验用户是否实名
     *
     * @param userAccountWithUser
     * @param withdrawConfig
     * @param withdrawRequestDto
     * @param withdrawSummary
     * @param withdrawAmount
     * @return
     */
    private Uni<WithdrawSummary> validWithdrawStepThree(UserAccountWithUser userAccountWithUser, WithdrawConfigDto withdrawConfig, WithdrawRequestDto withdrawRequestDto, WithdrawSummary withdrawSummary, BigDecimal withdrawAmount) {
        // 6. 校验提现金额是否满足渠道实名阈值要求（本次提现金额+累计提现金额）
        PaymentType paymentType = PaymentType.WEPAY; //重要：当前系统暂时固定只支持三方+微信提现
        BigDecimal realNameThreshold = withdrawConfig.getWithdrawRealNames().get(String.valueOf(paymentType.getCode()));
        if (realNameThreshold == null) {
            LOG.errorf("配置异常，通过提现渠道无法找到实名金额，请检查withdrawConfig.withdrawRealNames");
            return Uni.createFrom().failure(new WithdrawException("系统异常"));
        }
        // 计算本次提现金额加上累计提现金额的总和
        BigDecimal totalAmountAfterWithdraw = withdrawAmount.add(withdrawSummary.getTotalAmount());

        // 判断总和是否超过实名阈值，并且未实名，抛出异常
        if (totalAmountAfterWithdraw.compareTo(realNameThreshold) >= 0) {
            // 获取支付类型代码
            String paymentCode = String.valueOf(PaymentType.fromCode(withdrawRequestDto.getType()).getCode());
            // 调用实名校验方法并返回结果，确保校验生效
            return checkRealNameBy(paymentCode, userAccountWithUser.getUserId()).onItem().ifNull().failWith(() -> {
                LOG.errorf("用户实名校验失败：用户id = %s", userAccountWithUser.getUserId());
                return new WithdrawException("实名校验失败");
            }).onItem().transform(hasBind -> {
                if (!hasBind) {
                    throw new UserRealNameException("累计提现金额超过实名阈值，需先完成实名认证");
                }
                return withdrawSummary;
            });
        }
        // 未超过阈值或已实名，继续流程
        return Uni.createFrom().item(withdrawSummary);
    }

    /**
     * 第二部校验，校验用户是否绑定了提现方式
     *
     * @param userAccountWithUser
     * @param withdrawSummary
     * @return
     */
    private Uni<WithdrawSummary> validWithdrawStepTwo(UserAccountWithUser userAccountWithUser, WithdrawSummary withdrawSummary) {
        //校验用户提现方式是否绑定，如果未绑定，直接抛出异常
        PaymentType paymentType = PaymentType.WEPAY; //重要：当前系统暂时固定只支持三方+微信提现
        return checkUserBindStatusBy(String.valueOf(paymentType.getCode()), userAccountWithUser.getUserId()).onItem().ifNull().failWith(() -> {
            LOG.warnf("用户id = %s 未绑定提现方式", userAccountWithUser.getUserId());
            return new WithdrawException("提现方式尚未绑定");
        }).onItem().transform(hasBindWithdraw -> {
            if (!hasBindWithdraw) {
                throw new WithdrawException("提现方式尚未绑定");
            }
            return withdrawSummary;
        });
    }

    /**
     * 校验提现申请的第一步
     *
     * @param withdrawConfig
     * @param withdrawSummary
     * @param withdrawAmount
     * @return
     */
    private Uni<WithdrawSummary> validWithdrawStepOne(WithdrawConfigDto withdrawConfig, WithdrawSummary withdrawSummary, BigDecimal withdrawAmount) {

        //校验每日提现次数是否达到上限（本次提现也要算1次，且dailyMaxCount>0时才校验,判断条件是大于）
        Integer dailyCount = withdrawSummary.getDailySuccessCount() + withdrawSummary.getDailyPendingCount();
        Integer currentDailyCount = (dailyCount != null ? dailyCount : 0) + 1;
        if (withdrawConfig.getDailyMaxCount() > 0 && currentDailyCount > withdrawConfig.getDailyMaxCount()) {
            throw new WithdrawException("今日提现次数已达上限，每日最多可提现" + withdrawConfig.getDailyMaxCount() + "次");
        }

        // 5. 校验提现间隔时间
        LocalDateTime lastWithdrawTime = withdrawSummary.getLastTime();
        if (lastWithdrawTime != null && withdrawConfig.getWithdrawInterval() > 0) {
            long secondsSinceLastWithdraw = Duration.between(lastWithdrawTime, LocalDateTime.now()).getSeconds();
            if (secondsSinceLastWithdraw < withdrawConfig.getWithdrawInterval()) {
                throw new WithdrawException("提现间隔需大于" + withdrawConfig.getWithdrawInterval() + "秒，请稍后再试");
            }
        }


        // 7. 校验提现金额是否满足总金额限制配置（使用stripTrailingZeros().toPlainString()确保金额一致）
        List<WithdrawTotalLimitDto> totalLimits = withdrawConfig.getTotalLimits();
        if (totalLimits == null || totalLimits.size() == 0) {
            //如果没有该金额的系统配置，直接抛出异常。
            LOG.errorf("系统缺少提现金额限制，如不配置，将面临校验风险，请处理！");
            throw new WithdrawException("暂不提供提现");
        }
        String amountKey = new DecimalFormat("0.00").format(new BigDecimal(withdrawAmount.stripTrailingZeros().toPlainString())); //当前提现金额的String值
        Integer dailyLimit = 0; //当日该金额的累计提现配置
        Integer allCount = 0; //当日该金额的累计提现配置
        if (totalLimits != null && !totalLimits.isEmpty()) {
            boolean validAmount = false;

            //循环校验提现配置，从中命中该金额，找到其配置
            for (WithdrawTotalLimitDto limit : totalLimits) {
                String limitAmountStr = new DecimalFormat("0.00").format(new BigDecimal(limit.getAmount().stripTrailingZeros().toPlainString()));
                if (amountKey.equals(limitAmountStr)) {
                    validAmount = true;
                    dailyLimit = limit.getDailyLimit(); //该金额今日提现金额限制
                    allCount = limit.getAllCount(); //该金额的总提现次数上限
                    break;
                }
            }
            if (!validAmount) {
                throw new WithdrawException("提现金额不符合系统要求的金额选项");
            } else {
                //找到提现的金额，从已提面板中，找出该提现金额，并找出累计提现次数
                Map<String, WithdrawUserSummaryItem> amountItems = withdrawSummary.getAmountItems();

                if (amountItems == null) throw new WithdrawException("提现异常，错误代码8");
                Integer dailyAmountSuccessCount = 0; //某金额今日累计提现次数
                Integer totalAmountSuccessCount = 0;//某金额总提现此处
                if (amountItems != null && !amountItems.isEmpty()) {
                    if (!amountItems.containsKey(amountKey)) {
                        LOG.warnf("用户面板数据不存在该提现金额" + amountKey + "，第一次提现该金额");
                        //没有提过该金额，第一次提，校验总提现次数是否超过上限
                        dailyAmountSuccessCount = 1;
                        totalAmountSuccessCount = 1;
                    } else {
                        //提过该金额
                        WithdrawUserSummaryItem summaryItem = amountItems.get(amountKey);
                        LOG.debugf("用户面板数据存在该提现金额" + amountKey + "，今日第" + (summaryItem.getDailyCount() + 1) + "次提现该金额");
                        //判断用户今日正在处理和提现成功的次数
                        dailyAmountSuccessCount = summaryItem.getDailySuccessCount() + summaryItem.getDailyPendingCount() + 1; //用户今日某金额累计次数(成功+处理中，不含失败)，假定加1
                        totalAmountSuccessCount = summaryItem.getTotalCount() + 1; //用户该金额总提现次数(成功或正在处理)，假定+1
                    }
                }
                //当日累计提现次数（含本次）大于该金额当日限制提现次数
                if (dailyLimit > 0 && dailyAmountSuccessCount > dailyLimit) {
                    throw new WithdrawException("提现金额" + amountKey + "今日提现次数已达上限");
                }
                //判断累计成功总次数是否超过系统配置上限
                if (allCount > 0 && totalAmountSuccessCount > allCount) {
                    throw new WithdrawException("提现金额" + amountKey + "累计提现次数已达上限");
                }
            }
        }

        // 8. 联合校验：根据用户历史提现记录面板进行校验,key表示提现金额，value表示用户提现的面板数据

        return Uni.createFrom().item(withdrawSummary);
    }

    /**
     * 保存提现明细到数据库、并刷新用户累计提现的缓存。
     * 数据库、累计提现缓存处理后，推送自动提现到redis订阅，用于支付服务处理（发送某人、什么时、提现金额、申请时间等）
     *
     * @param amount 金额，传入的单位是分，在提现记录种要换算单位：元
     * @param isAuto 是否自动提现
     * @param userId 用户id
     * @return 成功或失败
     */
    private Uni<Boolean> saveWithdrawInfoAndCache(BigDecimal amount, Boolean isAuto, Long userId) {
        UserWithdrawal userWithdrawal = isAuto ? UserWithdrawal.createByAuto(userId, amount) : UserWithdrawal.createByManual(userId, amount);
        userWithdrawal.setAmount(amount.multiply(new BigDecimal("100")));
        // 先插入数据库，成功后刷新缓存
        LOG.infof("插入提现！");
        return withdrawRepository.addWithdraw(userWithdrawal).onItem().transformToUni(withdrawal -> {
            if (withdrawal != null) {
                if (isAuto) {
                    LOG.debugf("用户%s 提现:%s（元） 提现方式：%s", userId, amount, (isAuto ? "自动" : "手动"));
                    return withdrawRedisPubService.publishBatchTransferStatus(Arrays.asList(withdrawal)).onItem().transform(unused -> true);
                } else {
                    return Uni.createFrom().item(true);
                }
            }
            return Uni.createFrom().item(false);
        }).onItem().transformToUni(aBoolean -> {
            LOG.debugf("成功下单，刷新缓存");
            return withdrawCacheService.saveWithdrawSummary(userId).onItem().transformToUni(withdrawSummary -> Uni.createFrom().item(true));
        }).onFailure().invoke(throwable -> {
            throwable.printStackTrace();
            throw new RuntimeException("提现失败，系统异常");
        });
    }
}
